/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <unistd.h>

#include "local_net_dlist.h"
#include "local_net_def.h"
#include "local_net_utils.h"
#include "local_net_udp.h"

#ifdef L0_DEVICE
#include "lwip/sockets.h"
#include "lwip/netif.h"
#include "lwip/netifapi.h"
#include "lwip/ip4_addr.h"
#endif

#ifdef L2_DEVICE
#ifdef L1_DEVICE
#include "local_net_message.h"
#endif
#include <sys/prctl.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <errno.h>
#endif

#define IP_STR_LEN_MAX 16 // 255.255.255.255
#define IP_STR_LEN_MIN 8 // 0.0.0.0
#define LIMITED_BOARDCAST_IP "255.255.255.255"
#define UDP_COMM_PORT 50000
#define MSG_TYPE_BOARDCAST "broadcast"
#define DEVICE_ID_OBJECT_NAME "\"id\""
#ifdef L0_DEVICE
#define WLAN_NAME "wlan0"   // 无线设备使用："wlan0"
#elif defined(L2_DEVICE)
#define WLAN_NAME "eth0"    // l2设备插网线时候是eth0
#endif
#define UDP_MSG_SIZE_MAX 1024
#define CN_MINISECONDS_IN_SECOND 1000

#define MSG_SZ 1024
#define EXEC_SUCC 0
#define EXEC_FAILED (-1)

typedef struct {
    Node_t *list;
#ifdef L0_DEVICE
    osMutexId_t listMutex;
#elif defined(L2_DEVICE)
    pthread_mutex_t listMutex;
#endif
} LocalNetIPDList;
typedef LocalNetIPDList* IPDListHandle;

typedef struct {
#ifdef L0_DEVICE
    osThreadId_t boardcastThread;
    osThreadId_t sendThread;
    osThreadId_t recvThread;
    osMessageQueueId_t sendMsgQueue;
#elif defined(L2_DEVICE)
    pthread_t boardcastThread;
    pthread_t sendThread;
    pthread_t recvThread;
    key_t key;
    int sendMsgQueue;
#endif
    LocalNetThreadStatus boardcastThreadStatus;
    LocalNetThreadStatus sendThreadStatus;
    LocalNetThreadStatus recvThreadStatus;
    LocalNetUdpRecvCb recvCb;
    IPDListHandle ipdList;
} LocalNetUdpController;

typedef struct {
    NetData netData;
    char deviceId[DEVICE_ID_LEN];
} LocalNetUdpIPDNode;

typedef struct {
    uint32_t dataLen;
    uint8_t interval;
    char data[MSG_SZ];
} LocalNetUdpBoardcastDataDef;

static LocalNetUdpController g_localNetUdpCtl;

#ifdef L0_DEVICE
/**
 * @brief Convert miniseconds to system ticks
 * @param ms Indicates the mimiseconds to convert
 * @return Returns the corresponding ticks of specified time
 */
uint32_t Time2Tick(uint32_t ms)
{
    uint64_t ret;
    ret = ((uint64_t)ms * osKernelGetTickFreq()) / CN_MINISECONDS_IN_SECOND;
    return (uint32_t)ret;
}
#endif
static int DeviceIdCmp(const void* data1, const void* data2)
{
    int ret = -1;
    const LocalNetUdpIPDNode *destIPDNode = (const LocalNetUdpIPDNode*)data1;
    const LocalNetUdpIPDNode *srcIPDNode = (const LocalNetUdpIPDNode*)data2;

    if (!destIPDNode || !srcIPDNode) {
        LOG_E("destIPDNode or srcIPDNode is NULL!");
        return ret;
    }
    if (strlen(destIPDNode->deviceId) <= 0 || strlen(srcIPDNode->deviceId) <= 0) {
        LOG_E("data has been destroy!");
        return ret;
    }
    return strncmp(destIPDNode->deviceId, srcIPDNode->deviceId, DEVICE_ID_LEN);
}

static int NetDataCmp(const void* data1, const void* data2)
{
    int ret = -1;
    const LocalNetUdpIPDNode *destIPDNode = (const LocalNetUdpIPDNode*)data1;
    const LocalNetUdpIPDNode *srcIPDNode = (const LocalNetUdpIPDNode*)data2;

    if (!destIPDNode || !srcIPDNode) {
        LOG_E("destIPDNode or srcIPDNode is NULL!");
        return ret;
    }

#ifdef L0_DEVICE
    if (destIPDNode->netData.ip.s_addr == srcIPDNode->netData.ip.s_addr) {
        ret = 0;
    }
#elif defined(L2_DEVICE)
    if (destIPDNode->netData.ip == srcIPDNode->netData.ip) {
        ret = 0;
    }
#endif
    return ret;
}

static IPDListHandle LocalNetIPDListInit(void)
{
    IPDListHandle ipdList = (IPDListHandle)malloc(sizeof(LocalNetIPDList));
    if (!ipdList) {
        LOG_E("malloc ipd list handle failed!");
        return NULL;
    }
    ipdList->list = CreateDlist();
    if (!(ipdList->list)) {
        LOG_E("create ipd list failed!");
        goto IPD_LIST_INIT_ERR;
    }

#ifdef L0_DEVICE
    ipdList->listMutex = osMutexNew(NULL);
    if (!(ipdList->listMutex)) {
        LOG_E("create ipd list mutex failed!");
        goto IPD_LIST_INIT_ERR;
    }
#elif defined(L2_DEVICE)
    pthread_mutex_init(&(ipdList->listMutex), NULL);
#endif
    return ipdList;

IPD_LIST_INIT_ERR:
    if (ipdList) {
        free(ipdList);
    }
    return NULL;
}

static void LocalNetIPDListDeinit(IPDListHandle ipdList)
{
    DestroyDlist(&(ipdList->list));

#ifdef L0_DEVICE
    osMutexDelete(ipdList->listMutex);
#elif defined(L2_DEVICE)
    pthread_mutex_destroy(&(ipdList->listMutex));
#endif

    return;
}

static void printfHdlist(Node_t *head)
{
    Node_t *p = head;
    if (p != NULL) {
        SAMPLE_INFO("data->%s", p->data);
        p = p->after;
    } else {
        SAMPLE_INFO("p != NULL");
    }
}

static int8_t LocalNetIPDListNodeUpdate(const IPDListHandle ipdList, const LocalNetUdpIPDNode *pIpdNode)
{
    int8_t ret = -1;
    Node_t *node = NULL;
#ifdef L0_DEVICE
    osMutexAcquire(ipdList->listMutex, osWaitForever);
#elif defined(L2_DEVICE)
    pthread_mutex_lock(&(ipdList->listMutex));
#endif
    node = FindVdlist(ipdList->list, pIpdNode, DeviceIdCmp);    // 按值查找
    if (node) { // 存在该DeviceId的成员
        (void)memcpy_s(node->data, sizeof(LocalNetUdpIPDNode), pIpdNode, sizeof(LocalNetUdpIPDNode));
        ret = 0;
    } else { // 不存在该DeviceId的成员
        addLogs("Add new device IpdNode data to list!");
        if (!InsertHdlist(ipdList->list, pIpdNode, sizeof(LocalNetUdpIPDNode))) { // 插入头部
            addLogs("InsertHdlist success!");
            ret = 0;
        } else {
            LOG_E("InsertHdlist failed!");
        }
    }
#ifdef L0_DEVICE
    osMutexRelease(ipdList->listMutex);
#elif defined(L2_DEVICE)
    pthread_mutex_unlock(&(ipdList->listMutex));
#endif
    return ret;
}

static int8_t LocalNetIPDListDevIdMapNetData(const IPDListHandle ipdList, const char* pDeviceId, NetData *pNetData)
{
    int8_t ret = -1;
    Node_t *node = NULL;
    LocalNetUdpIPDNode ipdNode = {0};

    (void)memset_s(&ipdNode, sizeof(LocalNetUdpIPDNode), 0, sizeof(LocalNetUdpIPDNode));

    (void)strncpy_s(ipdNode.deviceId, DEVICE_ID_LEN, pDeviceId, DEVICE_ID_LEN);
#ifdef L0_DEVICE
    osMutexAcquire(ipdList->listMutex, osWaitForever);
#elif defined(L2_DEVICE)
    pthread_mutex_lock(&(ipdList->listMutex));
#endif
    node = FindVdlist(ipdList->list, &ipdNode, DeviceIdCmp);    // 按值查找
    if (node) { // 存在该DeviceId的成员
        (void)memcpy_s(&ipdNode, sizeof(LocalNetUdpIPDNode), node->data, sizeof(LocalNetUdpIPDNode));
        (void)memcpy_s(pNetData, sizeof(NetData), &(ipdNode.netData), sizeof(NetData));
        ret = 0;
    } else { // 不存在该DeviceId的成员
        addLogs("Can not find this member on the list!");
    }
    
#ifdef L0_DEVICE
    osMutexRelease(ipdList->listMutex);
#elif defined(L2_DEVICE)
    pthread_mutex_unlock(&(ipdList->listMutex));
#endif

    return ret;
}

static int8_t LocalNetIPDListNetDataMapDevId(const IPDListHandle ipdList, const NetData *pNetData, char* pDeviceId)
{
    int8_t ret = -1;
    Node_t *node = NULL;
    LocalNetUdpIPDNode ipdNode = {0};

    (void)memset_s(&ipdNode, sizeof(LocalNetUdpIPDNode), 0, sizeof(LocalNetUdpIPDNode));
    (void)memcpy_s(&(ipdNode.netData), sizeof(NetData), pNetData, sizeof(NetData));
#ifdef L0_DEVICE
    osMutexAcquire(ipdList->listMutex, osWaitForever);
#elif defined(L2_DEVICE)
    pthread_mutex_lock(&(ipdList->listMutex));
#endif
    node = FindVdlist(ipdList->list, &ipdNode, NetDataCmp);    // 按值查找
    if (node) { // 存在该netData的成员
        (void)memcpy_s(&ipdNode, sizeof(LocalNetUdpIPDNode), node->data, sizeof(LocalNetUdpIPDNode));
        (void)strncpy_s(pDeviceId, DEVICE_ID_LEN, ipdNode.deviceId, DEVICE_ID_LEN);
        ret = 0;
    } else { // 不存在该DeviceId的成员
        addLogs("Can not find this member on the list!");
    }

#ifdef L0_DEVICE
    osMutexRelease(ipdList->listMutex);
#elif defined(L2_DEVICE)
    pthread_mutex_unlock(&(ipdList->listMutex));
#endif

    return ret;
}

static int8_t LoaclNetUdpGetDevIdFromMsg(const char *msg, char* pDeviceId)
{
    int8_t ret = -1;
    char *pDeviceIdStr = NULL;

    if (!strstr(msg, MSG_TYPE_BOARDCAST)) {
        LOG_E("msg type is not boardcast!");
        return ret;
    }
    if (!(pDeviceIdStr = strstr(msg, DEVICE_ID_OBJECT_NAME))) {
        LOG_E("Have no id text in the msg!");
        return ret;
    }
    pDeviceIdStr = strstr(pDeviceIdStr, ":");
    pDeviceIdStr = strstr(pDeviceIdStr, "\""); // get "id" : ^"XXXX"
    if (*(pDeviceIdStr + DEVICE_ID_LEN) == '\"') { // [0]^"XXX...XXX[65]^"
        if (memcpy_s(pDeviceId, DEVICE_ID_LEN, pDeviceIdStr + 1, DEVICE_ID_LEN - 1) < 0) {
            return -1;
        }
        pDeviceId[DEVICE_ID_LEN - 1] = '\0';
        ret = 0;
    }
    return ret;
}

#ifdef L0_DEVICE
static void LocalNetUdpBoardCastThread(void *arg)
#elif defined(L2_DEVICE)
static void* LocalNetUdpBoardCastThread(void *arg)
#endif
{
    prctl(PR_SET_NAME, "LocalNetUdpBoardCastThread");
    int32_t optVal = 1;
    int32_t sendBytes = -1;
    struct sockaddr_in boardcastAddr;
    LocalNetUdpBoardcastDataDef *udpBoardcastData = (LocalNetUdpBoardcastDataDef*)arg;

    if (LOCAL_NET_THREAD_INIT == g_localNetUdpCtl.boardcastThreadStatus) {
        g_localNetUdpCtl.boardcastThreadStatus = LOCAL_NET_THREAD_RUNNING;
        addLogs("boardcastThreadStatus -> LOCAL_NET_THREAD_RUNNING.");
    }

    (void)memset_s(&boardcastAddr, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
    boardcastAddr.sin_family = AF_INET;
#ifdef L0_DEVICE
    boardcastAddr.sin_addr.s_addr = inet_addr(LIMITED_BOARDCAST_IP);
#elif defined(L2_DEVICE)
    boardcastAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
#endif
    boardcastAddr.sin_port = htons(UDP_COMM_PORT);
    while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.boardcastThreadStatus) {
        int32_t boardcastSocket = socket(AF_INET, SOCK_DGRAM, 0);
        if (boardcastSocket < 0) {
            LOG_E("Socket error return %d.", boardcastSocket);
            continue;
        }
        setsockopt(boardcastSocket, SOL_SOCKET, SO_BROADCAST | SO_REUSEADDR, &optVal, sizeof(int));
        while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.boardcastThreadStatus) {
#ifdef L0_DEVICE
            if ((sendBytes = sendto(boardcastSocket, udpBoardcastData->data, strlen(udpBoardcastData->data), 0,
                (struct sockaddr *)&boardcastAddr, sizeof(struct sockaddr))) == -1) {
                LOG_E("sendto fail, errno=%s\n",  strerror(errno));
                continue;
            }
            osDelay(Time2Tick(udpBoardcastData->interval * CN_MINISECONDS_IN_SECOND));
#elif defined(L2_DEVICE)
            // 广播
            if ((sendBytes = sendto(boardcastSocket, udpBoardcastData->data, strlen(udpBoardcastData->data), 0,
                (struct sockaddr *)&boardcastAddr, sizeof(struct sockaddr))) == -1) {
                continue;
            } else {
                SAMPLE_INFO("send ok sendBytes->%d", sendBytes);
            }
            sleep(udpBoardcastData->interval);
#endif
        }

        if (boardcastSocket >= 0) {
            close(boardcastSocket);
        }
    }

    if (udpBoardcastData) {
        free(udpBoardcastData);
    }

    if (LOCAL_NET_THREAD_RELEASE == g_localNetUdpCtl.boardcastThreadStatus) {
        g_localNetUdpCtl.boardcastThreadStatus = LOCAL_NET_THREAD_EXIT;
    } else {
        g_localNetUdpCtl.boardcastThreadStatus = LOCAL_NET_THREAD_ERR;
        LOG_E("boardcastThread release error!");
    }

#ifdef L0_DEVICE
    return;
#elif defined(L2_DEVICE)
    return NULL;
#endif
}

#ifdef L0_DEVICE
static void LocalNetUdpSendThread(void *arg)
#elif defined(L2_DEVICE)
static void* LocalNetUdpSendThread(void *arg)
#endif
{
#ifdef L0_DEVICE
    UNUSED(arg);
#endif
    
    prctl(PR_SET_NAME, "LocalNetUdpSendThread");
    int32_t sendBytes = -1;
    int32_t sendSocket = -1;
    struct sockaddr_in sendAddr;
    LocalNetUdpSendDataDef *udpSendData = NULL;
    if (LOCAL_NET_THREAD_INIT == g_localNetUdpCtl.sendThreadStatus) {
        g_localNetUdpCtl.sendThreadStatus = LOCAL_NET_THREAD_RUNNING;
    }

    while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.sendThreadStatus) {
        sendSocket = socket(AF_INET, SOCK_DGRAM, 0);
        if (sendSocket < 0) {
            LOG_E("Socket error\n");
            goto SEND_SOCK_ERR;
        }
        (void)memset_s(&sendAddr, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
        sendAddr.sin_family = AF_INET;
        while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.sendThreadStatus) {
#ifdef L0_DEVICE
            int32_t res = osMessageQueueGet(g_localNetUdpCtl.sendMsgQueue, &udpSendData, 0, 0);
            if (osOK != res) {
                addLogs("mqueue recv data failed!");
                osDelay(Time2Tick(OS_DELAY_300));
                continue;
            }
            sendAddr.sin_addr.s_addr = udpSendData->netData.ip.s_addr;
#elif defined(L2_DEVICE)
            udpSendData = (LocalNetUdpSendDataDef *)malloc(sizeof(LocalNetUdpSendDataDef));
            if (!udpSendData) {
                break;
            }
            udpSendData->dataType = 0;
#ifdef L1_DEVICE
            if (LocalNetCtlMsgRcv(udpSendData) < 0) {
#else
            if (msgrcv(g_localNetUdpCtl.sendMsgQueue, udpSendData,
                sizeof(LocalNetUdpSendDataDef), udpSendData->dataType, 0) < 0) {
#endif
                free(udpSendData);
                udpSendData = NULL;
                usleep(USLEEP_300);
                continue;
            }
            usleep(USLEEP_300);
            sendAddr.sin_addr.s_addr = udpSendData->netData.ip;
#endif
            sendAddr.sin_port = htons(UDP_COMM_PORT);

#ifdef L0_DEVICE
            if ((sendBytes = sendto(sendSocket, udpSendData->data, strlen(udpSendData->data), 0,
                (struct sockaddr*)&sendAddr, sizeof(struct sockaddr))) == -1) {
#elif defined(L2_DEVICE)
            if ((sendBytes = sendto(sendSocket, udpSendData->data, strlen(udpSendData->data), 0,
                (struct sockaddr*)&sendAddr, sizeof(sendAddr))) == -1) {
#endif
                goto SEND_SOCK_ERR;
            }
            if (udpSendData) {
                free(udpSendData);
                udpSendData = NULL;
            }

#ifdef L0_DEVICE
    osDelay(Time2Tick(OS_DELAY_300));
#elif defined(L2_DEVICE)
    usleep(USLEEP_300);
#endif
        }
SEND_SOCK_ERR:
        if (udpSendData) {
            free(udpSendData);
            udpSendData = NULL;
        }
        if (sendSocket >= 0) {
            close(sendSocket);
        }
        usleep(USLEEP_300);
    }

    if (LOCAL_NET_THREAD_RELEASE == g_localNetUdpCtl.sendThreadStatus) {
        g_localNetUdpCtl.sendThreadStatus = LOCAL_NET_THREAD_EXIT;
    } else {
        g_localNetUdpCtl.sendThreadStatus = LOCAL_NET_THREAD_ERR;
        LOG_E("sendThread release error!");
    }

#ifdef L0_DEVICE
    return;
#elif defined(L2_DEVICE)
    return NULL;
#endif
}

#ifdef L0_DEVICE
    static void LocalNetUdpRecvThread(void *arg)
#elif defined(L2_DEVICE)
    static void* LocalNetUdpRecvThread(void *arg)
#endif
{
    char *pRecvData = NULL;
    int32_t recvSocket = -1;
    int32_t recv_data_len = -1;
    struct sockaddr_in udpRecvAddr;
    struct sockaddr_in udpSenderAddr;
    LocalNetUdpIPDNode ipdNode = {0};
    socklen_t addrlen = sizeof(struct sockaddr);
    prctl(PR_SET_NAME, "LocalNetUdpRecvThread");

    if (LOCAL_NET_THREAD_INIT == g_localNetUdpCtl.recvThreadStatus) {
        addLogs("recvThreadStatus -> LOCAL_NET_THREAD_RUNNING");
        g_localNetUdpCtl.recvThreadStatus = LOCAL_NET_THREAD_RUNNING;
    }

    while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.recvThreadStatus) {
        recvSocket = socket(AF_INET, SOCK_DGRAM, 0);
        if (recvSocket < 0) {
            LOG_E("Socket error\n");
            goto RECV_SOCK_ERR;
        }

        pRecvData = (char*)malloc(UDP_MSG_SIZE_MAX);
        if (!pRecvData) {
            LOG_E("malloc deviceId failed! No memory\n");
            goto RECV_SOCK_ERR;
        }
        (void)memset_s(&udpRecvAddr, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
        (void)memset_s(&udpSenderAddr, sizeof(struct sockaddr_in), 0, sizeof(struct sockaddr_in));
        udpRecvAddr.sin_family = AF_INET;
        udpRecvAddr.sin_addr.s_addr = INADDR_ANY;
        udpRecvAddr.sin_port = htons(UDP_COMM_PORT);

        if (bind(recvSocket, (struct sockaddr *)&udpRecvAddr, sizeof(struct sockaddr)) < 0) {
            LOG_E("Unable to bind\n");
            goto RECV_SOCK_ERR;
        }

        while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.recvThreadStatus) {
            (void)memset_s(pRecvData, UDP_MSG_SIZE_MAX, 0, UDP_MSG_SIZE_MAX);
            recv_data_len = recvfrom(recvSocket, pRecvData, UDP_MSG_SIZE_MAX, 0,
                                     (struct sockaddr*)&udpSenderAddr, &addrlen);
            (void)memset_s(&ipdNode, sizeof(ipdNode), 0, sizeof(ipdNode));
#ifdef L0_DEVICE
            ipdNode.netData.ip.s_addr = udpSenderAddr.sin_addr.s_addr;
            ipdNode.netData.port = udpSenderAddr.sin_port;
#elif defined(L2_DEVICE)
            ipdNode.netData.ip = udpSenderAddr.sin_addr.s_addr;
            ipdNode.netData.port = udpSenderAddr.sin_port;
#endif
            if (!LoaclNetUdpGetDevIdFromMsg(pRecvData, ipdNode.deviceId)) {
                LocalNetIPDListNodeUpdate(g_localNetUdpCtl.ipdList, &ipdNode);
            }
            if (g_localNetUdpCtl.recvCb && !LocalNetIPDListNetDataMapDevId(g_localNetUdpCtl.ipdList,
                &(ipdNode.netData), ipdNode.deviceId)) {
                g_localNetUdpCtl.recvCb(pRecvData, ipdNode.deviceId);
            }
#ifdef L0_DEVICE
            osDelay(Time2Tick(OS_DELAY_300));
#elif defined(L2_DEVICE)
            usleep(USLEEP_300);
#endif
        }
RECV_SOCK_ERR:
        if (pRecvData) {
            free(pRecvData);
        }
        if (recvSocket >= 0) {
#ifdef L0_DEVICE
            closesocket(recvSocket);
#elif defined(L2_DEVICE)
            close(recvSocket);
#endif
        }
    }

    if (LOCAL_NET_THREAD_RELEASE == g_localNetUdpCtl.recvThreadStatus) {
        g_localNetUdpCtl.recvThreadStatus = LOCAL_NET_THREAD_EXIT;
    } else {
        g_localNetUdpCtl.recvThreadStatus = LOCAL_NET_THREAD_ERR;
    }

#ifdef L0_DEVICE
    return;
#elif defined(L2_DEVICE)
    return NULL;
#endif
}

int8_t LocalNetUdpSend(const char *msg, const char *pDeviceId)
{
    int8_t ret = -1;
    uint16_t msgLen = 0;
    LocalNetUdpSendDataDef *udpSendData;
    if ((!msg) || (!pDeviceId)) {
        LOG_E("msg or pDeviceId is null");
        return ret;
    }
    msgLen = strlen(msg) + 1;
    if (msgLen < 1 || msgLen > UDP_MSG_SIZE_MAX) {
        LOG_E("msg size is out of the limit!");
        return ret;
    }
    udpSendData = (LocalNetUdpSendDataDef*)malloc(sizeof(LocalNetUdpSendDataDef));
    if (udpSendData == NULL) {
        LOG_E("udpSendData malloc failed!");
        return ret;
    }
    (void)memset_s(udpSendData, sizeof(LocalNetUdpSendDataDef), 0, sizeof(LocalNetUdpSendDataDef));
    if (LocalNetIPDListDevIdMapNetData(g_localNetUdpCtl.ipdList,
        pDeviceId, &(udpSendData->netData))) {
        LOG_E("udp port and ip get failed!");
        goto MAKE_SEND_DATA_ERR;
    }

    udpSendData->dataLen = msgLen;
    (void)memcpy_s(udpSendData->data, MSG_SZ, msg, msgLen);

#ifdef L0_DEVICE
    if (osOK != osMessageQueuePut(g_localNetUdpCtl.sendMsgQueue, &udpSendData, 0, 0)) {
        LOG_E("osMessageQueuePut error.");
        goto MAKE_SEND_DATA_ERR;
    }
#elif defined(L2_DEVICE)
    udpSendData->dataType = 0;
#ifdef L1_DEVICE
    if (LocalNetCtlMsgSnd(udpSendData) < 0) {
#else
    if (msgsnd(g_localNetUdpCtl.sendMsgQueue, udpSendData, sizeof(LocalNetUdpSendDataDef), 0) < 0) {
#endif
        LOG_E("osMessageQueuePut error.");
        goto MAKE_SEND_DATA_ERR;
    }
    if (udpSendData) {
        free(udpSendData);
        udpSendData = NULL;
    }
#endif

    ret = 1;
    return ret;

MAKE_SEND_DATA_ERR:
    ret = -1;
    if (udpSendData) {
        free(udpSendData);
    }
    return ret;
}

int8_t LocalNetUdpBoardcast(const char *msg, uint8_t interval)
{
    int8_t ret = -1;
    int16_t msgLen = 0;
    LocalNetUdpBoardcastDataDef *udpBoardcastData = NULL;
    if (!msg) {
        LOG_E("msg or ipStr is null or out of the limit!");
        return ret;
    }

    msgLen = strlen(msg);
    if (msgLen < 0 || msgLen >= UDP_MSG_SIZE_MAX) {
        LOG_E("msg size is out of the limit!");
        return ret;
    }

    udpBoardcastData = (LocalNetUdpBoardcastDataDef*)malloc(sizeof(LocalNetUdpBoardcastDataDef));
    if (!udpBoardcastData) {
        LOG_E("udpBoardcastData malloc failed!");
        return ret;
    }
    (void)memset_s(udpBoardcastData, sizeof(LocalNetUdpBoardcastDataDef), 0, sizeof(LocalNetUdpBoardcastDataDef));
    udpBoardcastData->dataLen = msgLen;
    udpBoardcastData->interval = interval;
    (void)memcpy_s(udpBoardcastData->data, MSG_SZ, msg, msgLen);

    g_localNetUdpCtl.boardcastThreadStatus = LOCAL_NET_THREAD_INIT;

#ifdef L0_DEVICE
    osThreadAttr_t attr;
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.name = "UdpBoardcast";
    attr.stack_size = STACK_SIZE_0;
    attr.priority = STACK_PRIORITY;
    if (!(g_localNetUdpCtl.boardcastThread =
        osThreadNew((osThreadFunc_t)LocalNetUdpBoardCastThread, udpBoardcastData, &attr))) {
        LOG_E("Could not create the iotcloud thread.");
        goto MAKE_BOARDCAST_DATA_ERR;
    }
#elif defined(L2_DEVICE)
    if (pthread_create(&(g_localNetUdpCtl.boardcastThread), NULL, LocalNetUdpBoardCastThread,
        udpBoardcastData) != EXEC_SUCC) {
        LOG_E("Could not create the LocalNetUdpBoardCastThread thread.");
        goto MAKE_BOARDCAST_DATA_ERR;
    }
    pthread_detach(g_localNetUdpCtl.boardcastThread);
#endif
    ret = 0;
    return ret;

MAKE_BOARDCAST_DATA_ERR:
    ret = -1;
    if (udpBoardcastData) {
        free(udpBoardcastData);
    }
    return ret;
}

int8_t LocalNetUdpRecvCbReg(LocalNetUdpRecvCb recvCb)
{
    int8_t ret = -1;
    if (recvCb == NULL) {
        LOG_E("recvCb is null!");
        return ret;
    }
    g_localNetUdpCtl.recvCb = recvCb;
    ret = 0;
    return ret;
}

int8_t LocalNetUdpInit(void)
{
    int8_t ret = -1;
    const int readWriteMask = 666;
    g_localNetUdpCtl.ipdList = LocalNetIPDListInit();
    if (!(g_localNetUdpCtl.ipdList)) {
        LOG_E("ipdList init failed!");
    }

#ifdef L0_DEVICE
    g_localNetUdpCtl.sendMsgQueue =
        osMessageQueueNew(MQ_MSG_NUM_MAX, MQ_MSG_SIZE_MAX, (osMessageQueueAttr_t*)NULL);
    if (!g_localNetUdpCtl.sendMsgQueue) {
        LOG_E("osMessageQueueNew faild!");
        return ret;
    }
    g_localNetUdpCtl.sendThreadStatus = LOCAL_NET_THREAD_INIT;
    osThreadAttr_t attr;
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.name = "UdpSend";
    attr.stack_size = STACK_SIZE_4;
    attr.priority = STACK_PRIORITY;
    if ((g_localNetUdpCtl.sendThread =
        osThreadNew((osThreadFunc_t)LocalNetUdpSendThread, NULL, &attr)) == NULL) {
        LOG_E("Could not create the LocalNetUdpSend thread.");
        return ret;
    }

    g_localNetUdpCtl.recvThreadStatus = LOCAL_NET_THREAD_INIT;
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.name = "UdpRecv";
    attr.stack_size = STACK_SIZE_4;
    attr.priority = STACK_PRIORITY;
    if ((g_localNetUdpCtl.recvThread =
        osThreadNew((osThreadFunc_t)LocalNetUdpRecvThread, NULL, &attr)) == NULL) {
        LOG_E("Could not create the LocalNetUdpRecv thread.");
        return ret;
    }
#elif defined(L2_DEVICE)
#ifdef L1_DEVICE
    if (MstInit() < 0) {
        SAMPLE_ERROR("MstInit");
        return -1;
    }
#else
    if ((g_localNetUdpCtl.key = ftok("./", 0xa)) < 0) {
        LOG_E("osMessageQueueNew faild!");
    }
    g_localNetUdpCtl.sendMsgQueue = msgget(g_localNetUdpCtl.key, IPC_CREAT | readWriteMask);
    if (g_localNetUdpCtl.sendMsgQueue < 0) {
        LOG_E("osMessageQueueNew faild!");
    }
#endif
    g_localNetUdpCtl.sendThreadStatus = LOCAL_NET_THREAD_INIT;
    if (pthread_create(&(g_localNetUdpCtl.sendThread), NULL, LocalNetUdpSendThread, NULL) != EXEC_SUCC) {
        LOG_E("Could not create the LocalNetDevOfflineJudge thread.");
    }
    pthread_detach(g_localNetUdpCtl.sendThread);

    g_localNetUdpCtl.recvThreadStatus = LOCAL_NET_THREAD_INIT;
    if (pthread_create(&(g_localNetUdpCtl.recvThread), NULL, LocalNetUdpRecvThread, NULL) != EXEC_SUCC) {
        LOG_E("Could not create the LocalNetCommunicationCtl thread.");
    }
    pthread_detach(g_localNetUdpCtl.recvThread);
#endif

    ret = 0;
    return ret;
}

int8_t LocalNetUdpDeinit(void)
{
    int8_t ret = -1;
    uint8_t iCnt = 0;
    const int retryCnt = 5;
    const int osDelay10ms = 10;
    const int usleep10ms = 10000;
    g_localNetUdpCtl.boardcastThreadStatus = LOCAL_NET_THREAD_RELEASE;
    g_localNetUdpCtl.sendThreadStatus = LOCAL_NET_THREAD_RELEASE;
    g_localNetUdpCtl.recvThreadStatus = LOCAL_NET_THREAD_RELEASE;

    LocalNetIPDListDeinit(g_localNetUdpCtl.ipdList);
#ifdef L0_DEVICE
    osMessageQueueDelete(g_localNetUdpCtl.sendMsgQueue);
    while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.boardcastThreadStatus) {
        iCnt++;
        osDelay(Time2Tick(osDelay10ms));
        if (iCnt > retryCnt) {
            g_localNetUdpCtl.boardcastThreadStatus = LOCAL_NET_THREAD_ERR;
        }
    }
    osThreadTerminate(g_localNetUdpCtl.boardcastThread);
    while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.sendThreadStatus) {
        iCnt++;
        osDelay(Time2Tick(osDelay10ms));
        if (iCnt > retryCnt) {
            g_localNetUdpCtl.sendThreadStatus = LOCAL_NET_THREAD_ERR;
        }
    }
    osThreadTerminate(g_localNetUdpCtl.sendThread);
    while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.recvThreadStatus) {
        iCnt++;
        osDelay(Time2Tick(osDelay10ms));
        if (iCnt > retryCnt) {
            g_localNetUdpCtl.recvThreadStatus = LOCAL_NET_THREAD_ERR;
        }
    }
    osThreadTerminate(g_localNetUdpCtl.recvThread);
#elif defined(L2_DEVICE)
    while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.boardcastThreadStatus) {
        iCnt++;
        usleep(usleep10ms);
        if (iCnt > retryCnt) {
            g_localNetUdpCtl.boardcastThreadStatus = LOCAL_NET_THREAD_ERR;
        }
    }
 
    while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.sendThreadStatus) {
        iCnt++;
        usleep(usleep10ms);
        if (iCnt > retryCnt) {
            g_localNetUdpCtl.sendThreadStatus = LOCAL_NET_THREAD_ERR;
        }
    }

    while (LOCAL_NET_THREAD_RUNNING == g_localNetUdpCtl.recvThreadStatus) {
        iCnt++;
        usleep(usleep10ms);
        if (iCnt > retryCnt) {
            g_localNetUdpCtl.recvThreadStatus = LOCAL_NET_THREAD_ERR;
        }
    }
#endif
    ret = 0;
    return ret;
}

/**
 * @brief Get the Local Mac Addr
 *
 * @param ethName 网卡设备名称
 * @param macAddr 获取到的mac地址
 * @param macSize macAddr的总大小
 * @return int
 */
int getLocalMacAddr(const char *ethName, char *macAddr, int macSize)
{
    (void)memset_s(macAddr, macSize, 0, macSize);
    (void)strcpy_s(macAddr, macSize, "40c245ff0485");

    return 0;
}
